#!/usr/bin/python
import os
import sys
import struct
import time


def parseObj2(ginfo):
    info = {}
    group_list = []
    Points = 0
    ginfo = [line.strip() for line in ginfo]

    for line in ginfo:
        if line.startswith("v "):
            Points += 1
        if line.startswith("g "):
            group = line.split(" ")[1]
            # underscore is needed because H. sees new line at the end of obj_group as _
            group_list.append(group+"_")
        
    info['group_list'] = group_list
    info['Points']     = Points

    return info


def parseObj(ginfo):
    """Parses obj using $HB/ginfo"""

    info = {}
    groups = {}
    group_list = []
    dash = 0
    segments = []

    # Strip spaces and empty lines:
    ginfo = [line.strip() for line in ginfo if len(line) > 0]
    for line in ginfo[:4]:
        info[line.split()[1]] = int(line.split()[0])

    # Find groups
    for line in range(len(ginfo)):
        if ginfo[line].startswith("______"):
            segments.append(line)
            dash += 1
            

    for line in ginfo[segments[0]+2: segments[1]]:
        group_list.append(line.split()[-1])
        groups[line.split()[-1]] = line.split()[0]

    info['prim_groups'] = groups
    # We need this to maintain order:
    info['group_list'] = group_list
    return info


def getHeaders(pc2s):
    """Reads headers from a sequence of pc2 files."""  
    headers = {}
    for key in pc2s.keys():
        h = {}
        file = open(pc2s[key], 'rb')
        sig  = struct.unpack('12c', file.read(12))
        fileVersion, numPoints, startFrame, sampleRate, numSamples = struct.unpack('iiffi', file.read(20))
        file.close()
        h['magic']       = "".join(sig)
        h['fileVersion'] = fileVersion
        h['numPoints']   = numPoints 
        h['startFrame']  = startFrame
        h['sampleRate']  = sampleRate
        h['numSamples']  = numSamples
        headers[key] = h
    return headers


def main():

    if len(sys.argv) < 4:
        print "Usage:"
        print "\tmergepc2 object.obj objects_parts.*.pc2 object.pc2\n"
        print "\tProcess takes approx. 5sec. for 20 groups = 12K points in total."
        sys.exit()

    obj = sys.argv[1]
    pc2 = sys.argv[2:-1]
    out = sys.argv[-1]

    # Parse provided obj:
    #hb    = os.getenv("HB", "/opt/package/houdini_11.1.118")
    #ginfo = os.popen("%s %s" % (os.path.join(hb, "ginfo"), obj)).readlines()
    ginfo  = open(obj, 'r').readlines()
    detail = parseObj2(ginfo)
    #print detail

    # Build dict {group:file}
    pc2s = {}
    for f in pc2:
        #group_name = os.path.splitext(f)[0]
        group_name = f.split(".")[-2]
        pc2s[group_name+"_"] = f

    # Read in headers:
    headers = getHeaders(pc2s)
    #print headers

    # Construct new header
    # TODO: we should check if all headers are the same:
    magic   = headers[headers.keys()[0]]['magic']
    samples = headers[headers.keys()[0]]['numSamples']
    points  = headers[headers.keys()[0]]['numPoints']
    rate    = headers[headers.keys()[0]]['sampleRate']
    start   = headers[headers.keys()[0]]['startFrame']
    
    # We can't rely on points num from pc2:
    numPoints = detail['Points']

    #print  pointsu, start, rate, samples
    newHeader = struct.pack("12ciiffi", 'P','O','I','N','T','C','A','C','H','E','2','\x00', \
                            1, numPoints, start, rate, samples)
    
    # Write down a header:
    PC = open(out, "wb")
    PC.write(newHeader)
    PC.close()
    
    # Reopen output & seek to skip a header: 
    PC = open(out, "ab+")
    PC.seek(32)  
        

    #Print details.
    #TODO: make progress working (with subprocess?)
    print "Details: "
    print "Obj: %s,\n pc2: %s, \n output: %s " % (obj, pc2, out)
    print detail
    print pc2s

    # Start counting...
    time.clock()
    # Iterate over samples:
    for sample in range(samples):
        #print sample,
        for group in detail['group_list']:
            file = pc2s[group]
            points = headers[group]['numPoints']
            offset = points*3*4*sample
            size   = points*3
            format = "%sf" % size
            f = open(file, 'rb')
            f.seek(32+offset)
            try:
                floats = struct.unpack(format, f.read(size*4))
                PC.write(struct.pack('%sf' % len(floats), *floats))
            except:
                print "Can't deal with %s at %s sample. Exit now." % (file, sample)
                sys.exit()
            
            f.close()
    PC.close()
    print "Process took: %s" % time.clock()



if __name__ == "__main__": main()

